﻿namespace Services
{
    using System.Collections.Generic;
    using System.Linq;
    using DAL;
    using Entities;
    using System.Data.Objects;
    using System.ComponentModel.Composition;
    using System;
    using System.Linq.Expressions;

    [Export(typeof(IAuditedOperationsService))]
    public class AuditedOperationsService : BaseReportingService, IAuditedOperationsService
    {
        #region Member Variables

        protected readonly IAuditedOperationRepository _auditedOperationRepository;
        const int PAGESIZE = 20;

        #endregion

        #region Constructor

        [ImportingConstructor]
        public AuditedOperationsService(IAuditedOperationRepository auditedOperationRepository)
        {
            this._auditedOperationRepository = auditedOperationRepository;
        }

        #endregion

        #region IAuditedOperationsService

        public IEnumerable<AuditedOperation> SearchFileActionsPaged(FilterOperations filter, int page, out int totalCount, string columnName, bool isDescending)
        {
            var operations = SearchFileActions(filter).AsQueryable();

            return PageRecords(operations, page, out totalCount, columnName, isDescending, PAGESIZE);
        }

        public IEnumerable<AuditedOperation> SearchFileActions(FilterOperations filter)
        {
            var operations = from o in _auditedOperationRepository.Context.AuditedOperations
                             select o;

            if (filter.operationType != OperationType.All)
            {
                operations = from o in operations
                             where o.OperationType == (short)filter.operationType && o.IsDir == filter.IsDir
                             select o;
            }

            return FilterOperations(filter, operations);
        }

        public IEnumerable<AuditedOperation> SearchOffHours(FilterOperations filter)
        {
            var operations = from o in _auditedOperationRepository.Context.AuditedOperations
                             where o.ReceivedTime.Value.Hour > 16 || o.ReceivedTime.Value.Hour < 9
                             select o;

            return FilterOperations(filter, operations);
        }

        public IEnumerable<AuditedOperation> GetByBotName(string botName, FilterOperations filter)
        {
            int totalCount = 0;
            return GetByBotName(botName, filter, ref totalCount);
        }

        public IEnumerable<AuditedOperation> GetByBotName(string botName, FilterOperations filter, ref int totalCount, string sort = null, string sortDir = null, int page = 1)
        {
            var operations = _auditedOperationRepository.Context.GetByBotName(botName: botName,
                                                              periodStart: filter.StartDate,
                                                              periodEnd: filter.EndDate,
                                                              user: filter.User,
                                                              maxRecordsBack: null,
                                                              sortColumn: sort,
                                                              sortDirection: sortDir)
                                                              .AsQueryable();
            var result = operations.ToList();
            totalCount = result.Count;
            return result.Skip((page - 1) * PAGESIZE).Take(PAGESIZE);
        }

        public IEnumerable<AccessedExtension> GetTopTenAccessedExtensions(FilterOperations filter, out int totalNumber)
        {
            int topN = 10;
            var filteredOperations = FilterOperations(filter, _auditedOperationRepository.Context.AuditedOperations);
            totalNumber = filteredOperations.Count();
            var groupedExtensions = from e in filteredOperations
                                    group e by e.FileType into g
                                    let count = g.Count()
                                    orderby count descending
                                    select new AccessedExtension { FileType = g.Key, Count = count };
            var topTenExtensions = groupedExtensions.Take(topN).ToList();

            if (groupedExtensions.Count() > topN)
            {
                var otherExtensionsCount = groupedExtensions.Skip(topN).Sum(o => o.Count);
                topTenExtensions.Add(new AccessedExtension { FileType = "Other Extensions", Count = otherExtensionsCount });
            }

            return topTenExtensions;
        }

        public IEnumerable<UserSummary> GetMostActiveUsers(FilterOperations filter)
        {
            var filteredOperations = FilterOperations(filter, _auditedOperationRepository.Context.AuditedOperations);

            return filteredOperations.GroupBy(o => o.UserAccount).OrderByDescending(o => o.Count()).Select(o => new UserSummary { UserName = o.Key, NumberOfFiles = o.Count() });
        }

        public IEnumerable<AuditedOperation> GetDeletionCompliance(TimeSpans ts, int value)
        {
            DateTime startDate = GetStartDate(ts, value);

            return _auditedOperationRepository.Find(a => EntityFunctions.TruncateTime(a.ReceivedTime) >= EntityFunctions.TruncateTime(startDate) && a.OperationType == (short)OperationType.Deleted);
        }

        public IEnumerable<DirectoryCount> GetMostAccessedDirectories(FilterOperations filter)
        {
            var operations = from o in _auditedOperationRepository.Context.AuditedOperations
                             where o.IsDir == true
                             select o;

            return FilterOperations(filter, operations)
                .GroupBy(o => o.ObjectName)
                .OrderByDescending(o => o.Count())
                .Select(o => new DirectoryCount
                {
                    Directory = o.Key,
                    Count = o.Count()
                })
                .ToArray();
        }

        public IEnumerable<AuditedOperation> GetAccessHistory(TimeSpans ts, int value)
        {
            DateTime startDate = GetStartDate(ts, value);

            return _auditedOperationRepository.Find(a => EntityFunctions.TruncateTime(a.ReceivedTime) >= EntityFunctions.TruncateTime(startDate));
        }

        public IEnumerable<AuditedOperation> GetChangedFiles(FilterOperations filter)
        {
            var changedFiles = from o in _auditedOperationRepository.Context.AuditedOperations
                               select o;

            changedFiles = GetChangedFiles(changedFiles);

            return FilterOperations(filter, changedFiles);
        }

        public FileOperationsSummary GetFileOperationsSummary(FilterOperations filterOperations)
        {
            var allOperations = from o in _auditedOperationRepository.Context.AuditedOperations
                                select o;

            allOperations = FilterOperations(filterOperations, allOperations);

            return new FileOperationsSummary
            {
                Deleted = allOperations.Where(o => o.OperationType == (short)OperationType.Deleted).Count(),
                Renamed = allOperations.Where(o => o.OperationType == (short)OperationType.Rename).Count(),
                Moved = allOperations.Where(o => o.OperationType == (short)OperationType.Move).Count(),
                Changed = GetChangedFiles(allOperations).Count(),
                Created = allOperations.Where(o => o.OperationType == (short)OperationType.Create).Count(),
                OwnerChanged = allOperations.Where(o => o.OperationType == (short)OperationType.OwnerChange).Count(),
                PermissionChanged = allOperations.Where(o => o.OperationType == (short)OperationType.PermissionChange).Count(),
            };
        }

        public IEnumerable<DirectoryOperations> GetDeleteOperationsGroupedByDirectory(FilterOperations filter)
        {
            var deletedFiles = from o in _auditedOperationRepository.Context.AuditedOperations
                               where o.OperationType == (short)OperationType.Deleted
                               select o;

            return FilterOperations(filter, deletedFiles)
                .GroupBy(o => o.FullPathName)
                .Select(s => new DirectoryOperations
            {
                Directory = s.Key,
                Operations = s
            }
            );
        }

        #endregion

        #region Private Methods

        private DateTime GetStartDate(TimeSpans ts, int value)
        {
            DateTime startDate = DateTime.Now.AddDays(-value);

            switch (ts)
            {
                case TimeSpans.Weeks:
                    startDate = DateTime.Now.AddDays(-value * 7);
                    break;
                case TimeSpans.Months:
                    startDate = DateTime.Now.AddMonths(-value);
                    break;
                case TimeSpans.Years:
                    startDate = DateTime.Now.AddYears(-value);
                    break;
                default:
                    break;
            }
            return startDate;
        }

        private IQueryable<AuditedOperation> GetChangedFiles(IQueryable<AuditedOperation> operations)
        {
            return from o in operations
                   where o.OperationType == (short?)OperationType.Rename || (o.DeltaSize.HasValue && o.DeltaSize != 0 && o.OperationType == (short?)OperationType.Close)
                   select o;
        }

        #endregion
    }
}